Chapter 6 Data Visualizations

6.1 Room Temperature Reduction

6.1.1 Task

As part of an energy optimization, you lower the room temperatures in a room and would now like to show the reduction effect using the time series of the room temperature sensor. In the example below you make two optimizations at different dates.

You want to create a time series plot with

  • the daily median, min and max value

  • the overall median of each period

  • the desired setpoint

6.1.2 Basis

  • Time series data from e.g. a temperature sensor with unaligned time intervals

6.1.3 Solution

library(dplyr)
library(lubridate)
library(dygraphs)
library(xts)
library(redutils)
library(RColorBrewer)

# Settings
tempSetpoint = 22.0

startDate = "2018-11-01"
endDate = "2019-02-01"

optiDate1 = "2018-12-17"
optiLabel1 = "Optimization I"

optiDate2 = "2019-01-03"
optiLabel2 = "Optimization II"

optiDelayDays = 5

# read and print data
df <- read.csv("https://github.com/hslu-ige-laes/edar/raw/master/sampleData/flatTempHum.csv",
                 stringsAsFactors=FALSE,
                 sep =";")

# select temperature and remove empty cells
df <- df %>% select(time, FlatA_Temp) %>% na.omit()

# create column with day for later grouping
df$time <- parse_date_time(df$time, "YmdHMS", tz = "Europe/Zurich")
df$day <- as.Date(cut(df$time, breaks = "day"))
df$day <- as.Date(as.character(df$day,"%Y-%m-%d"))

# filter time range
df <- df %>% filter(day > startDate, day < endDate)

# calculate daily median, min and max of temperature
df <- df %>%
  group_by(day) %>%
  mutate(minDay = min(as.numeric(FlatA_Temp)),
         medianDay = median(as.numeric(FlatA_Temp)),
         maxDay = max(as.numeric(FlatA_Temp))
         ) %>%
  ungroup()

# shrink down to daily values and remove rows with empty values
df <- df %>% select(day, medianDay, minDay, maxDay) %>% unique() %>% na.omit()

# calculate medians for time ranges
df <- df %>%
  mutate(period = ifelse(day >= startDate & day <= optiDate1,
                         "Baseline",
                         ifelse((day >= (as.Date(optiDate1) + optiDelayDays))
                                & (day <= optiDate2),
                                "Opti1",
                                ifelse((day >= (as.Date(optiDate2) + optiDelayDays))
                                & (day <= endDate),
                                "Opti2",
                                NA)
                         )))

df <- df %>%
  group_by(period) %>%
  mutate(medianPeriod = ifelse(is.na(period), NA, median(medianDay))) %>% 
  ungroup() %>% 
  select(-period)

# create xts object for plotting
plotdata <- xts( x=df[,-1], order.by=df$day)

# plot graph
dygraph(plotdata, main = "Room Temperature Reduction") %>%
  dyAxis("x", drawGrid = FALSE) %>%
  dySeries(c("minDay", "medianDay", "maxDay"),
           label = "Temperature") %>%
  dySeries(c("medianPeriod"),
           label = "Median Period",
           strokePattern = "dashed") %>%
  dyOptions(colors = RColorBrewer::brewer.pal(3, "Set2")) %>%
  dyEvent(x = optiDate1,
          label = optiLabel1,
          labelLoc = "bottom",
          color = "slategray",
          strokePattern = "dotted") %>% 
  dyEvent(x = optiDate2,
          label = optiLabel2,
          labelLoc = "bottom",
          color = "slategray",
          strokePattern = "dotted") %>% 
  dyLimit(tempSetpoint,
          color = "red",
          label = "Target Setpoint") %>% 
  dyRangeSelector() %>% 
  dyLegend(show = "always")

6.1.4 Discussion

In this example we used the dygraph package to create the graph. This package is fast and allows to show a rangeslider on the bottom of the graph. The exact same graph but without a slider is as well possible with ggplot.

Please note that the calculation of the periodic median after optimization I and II starts delayed because it takes time until the building has cooled down.

6.2 Energy Consumption Before/After per Month

6.2.1 Task

Energy Consumption Before/After Optimization

Figure 6.1: Energy Consumption Before/After Optimization

6.2.2 Basis

6.2.3 Solution

6.2.4 Discussion

6.3 Building Energy Signature

6.3.1 Task

You want to create a scatter plot with

  • the daily mean outside temperature on the x-axis

  • the daily energy consumption on the y-axis

  • points colored according to season

6.3.2 Basis

  • Two separate csv files with time series data from the outside temperature and the energy data with unaligned time intervals

  • Energy consumption time series from a energy meter with steadily increasing meter values

6.3.3 Solution

After reading in the two time series the data has to get aggregated per day and then merged. Note that during the aggregation of the energy data you have to calculate the daily conspumption from the steadiliy increasing meter values as well.

Create a new script, copy/paste the following code and run it:

library(ggplot2)
library(plotly)
library(dplyr)
library(redutils)
library(lubridate)

# load time series data and aggregate daily mean values
dfOutsideTemp <- read.csv("https://github.com/hslu-ige-laes/edar/raw/master/sampleData/centralOutsideTemp.csv",
                          stringsAsFactors=FALSE,
                          sep =";")

dfOutsideTemp$time <- parse_date_time(dfOutsideTemp$time,
                                      order = "YmdHMS",
                                      tz = "Europe/Zurich")

dfOutsideTemp$day <- as.Date(cut(dfOutsideTemp$time, breaks = "day"))

dfOutsideTemp <- dfOutsideTemp %>%
  group_by(day) %>%
  mutate(tempMean = mean(centralOutsideTemp)) %>%
  ungroup()

dfOutsideTemp <- dfOutsideTemp %>%
  select(day, tempMean) %>%
  unique() %>%
  na.omit()

dfHeatEnergy <- read.csv("https://github.com/hslu-ige-laes/edar/raw/master/sampleData/centralHeating.csv",
                         stringsAsFactors=FALSE,
                         sep =";")

dfHeatEnergy <- dfHeatEnergy %>%
  select(time, energyHeatingMeter) %>%
  na.omit()

dfHeatEnergy$time <- parse_date_time(dfHeatEnergy$time,
                                     orders = "YmdHMS",
                                     tz = "Europe/Zurich")

dfHeatEnergy$day <- as.Date(cut(dfHeatEnergy$time, breaks = "day"))

dfHeatEnergy <- dfHeatEnergy %>%
  group_by(day) %>%
  mutate(energyMax = max(energyHeatingMeter)) %>%
  ungroup()

dfHeatEnergy <- dfHeatEnergy %>%
  select(day, energyMax) %>%
  unique() %>%
  na.omit()

dfHeatEnergy <- dfHeatEnergy %>% 
  mutate(energyCons = energyMax - lag(energyMax)) %>%
  select(-energyMax) %>%
  na.omit()

# merge the data in a tidy format
df <- merge(dfOutsideTemp, dfHeatEnergy, by = "day")

# calculate season
df <- df %>% mutate(season = redutils::getSeason(df$day))

# static chart with ggplot
p <- ggplot2::ggplot(df) +
  ggplot2::geom_point(aes(x = tempMean,
                          y = energyCons,
                          color = season,
                          alpha = 0.1,
                          text = paste("</br>Date:  ", as.Date(df$day),
                                       "</br>Temp: ", round(df$tempMean, digits = 1), "\u00B0C",
                                       "</br>Energy: ", round(df$energyCons, digits = 0), "kWh/d",
                                       "</br>Season: ", df$season))
                      ) +
  scale_color_manual(values=c("#440154", "#2db27d", "#fde725", "#365c8d")) +
  ggtitle("Building Energy Signature") +
        theme_minimal() +
        theme(
          legend.position="none",
          plot.title = element_text(hjust = 0.5)
        )

# interactive chart
plotly::ggplotly(p, tooltip = c("text")) %>%
  layout(xaxis = list(title = "Outside temperature (\u00B0C)",
                      range = c(min(-5,min(df$tempMean)), max(35,max(df$tempMean))), zeroline = F),
         yaxis = list(title = "Daily energy consumption (kWh/d)",
                      range = c(-5, max(df$energyCons) + 10)),
         showlegend = TRUE
         ) %>%
  plotly::config(displayModeBar = FALSE, displaylogo = FALSE)

6.4 Daily Energy Profiles

## [1] "English_United States.1252"
library(plotly)
library(dplyr)
library(lubridate)

# load time series data
df <- read.csv("https://github.com/hslu-ige-laes/edar/raw/master/sampleData/eboBookEleMeter.csv",
               stringsAsFactors=FALSE,
               sep =";")

# rename column names
colnames(df) <- c("timestamp", "meterValue")

df$timestamp <- parse_date_time(df$timestamp,
                                orders = "YmdHMS",
                                tz = "Europe/Zurich")
df$timestamp <- force_tz(df$timestamp, tzone = "UTC")

# uncomment to filter time range if necessary
#df <- df %>% filter(timestamp > "2015-03-01 00:00:00", timestamp < "2015-04-01 00:00:00")

# Fill missing values with NA
grid.df <- data.frame(timestamp = seq(min(df$timestamp, na.rm = TRUE),
                                      max(df$timestamp, na.rm = TRUE),
                                      by = "15 mins"))
df <- merge(df, grid.df, all = TRUE)

# convert steadily counting energy meter value from kWh to power in kW
df <- df %>% 
  mutate(value = (meterValue - lag(meterValue))*4) %>%
  select(-meterValue) %>%
  na.omit()

# remove negative values which occur beause of change summer/winter time
df <- df %>% filter(value >= 0)

# add metadata for later grouping and visualization purposes
df$x <- hour(df$timestamp) + minute(df$timestamp)/60 + second(df$timestamp) / 3600
df$weekday <- weekdays(df$timestamp)
df$weekday <- factor(df$weekday, c("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday", "Sunday"))
df$day <- as.Date(df$timestamp, format = "%Y-%m-%d  %H:%M:%S")

df <- df %>% mutate(value = ifelse(x == 0.00, NA, df$value))

# plot graph with all time series
rangeX <- seq(0,24,0.25)
maxValue <- max(df$value, na.rm = TRUE)*1.05

df %>% 
  highlight_key(~day) %>%
  plot_ly(x=~x,
          y=~value,
          color=~weekday,
          type="scatter",
          mode="lines",
          line = list(width = 1),
          alpha = 0.15,
          colors = "dodgerblue4",
          text = ~day,
          hovertemplate = paste("Time: ", format(df$timestamp, "%H:%M"),
                                "<br>Date: ", format(df$timestamp, "%Y-%m-%d"),
                                "<br>Value: %{y:.0f}")) %>%
  # workaround with add_trace to have fixed y axis when selecting a dedicated day
  add_trace(x = 0, y = 0, type = "scatter", showlegend = FALSE, opacity=0) %>%
  add_trace(x = 24, y = maxValue, type = "scatter", showlegend = FALSE, opacity=0) %>%
  layout(title = "Superimposed Profiles of Power Consumption per 15 min",
         showlegend = TRUE,
         xaxis = list(
           title = "Hour of day",
           range = rangeX,
           tickvals = list(0, 3, 6, 9, 12, 15, 18, 21),
           showline=TRUE
           ),
           yaxis = list(
             title = "Power (kW)",
             range = c(0, maxValue)
           )
         ) %>% 
  highlight(on = "plotly_hover",
            off = "plotly_doubleclick",
            color = "orange",
            opacityDim = 1.0,
            selected = attrs_selected(showlegend = FALSE)) %>% # this hides elements in the legend
  plotly::config(modeBarButtons = list(list("toImage")), displaylogo = FALSE)

Next we want to create an overview with the mean values for each 15 minute slot per day.

Append the following code at the end of your script:

6.5 Mollier hx Diagram

6.5.1 Task

You want to plot a mollier h-x diagram with

  • scatter plot of temperature- and humidity sensor data (mean values per day)

  • points colored according to season

  • comfort zone

6.5.2 Basis

  • A csv file with time series from multiple temperature and humidity sensors in °C and %rH

6.5.3 Solution

The sensor data is not in a constant intervall and not yet aggregated. So after reading in the time series the data has to get filtered and aggregated per day.

Finally use the plot function mollierHxDiagram from the redutils package (R Energy Data Utilities). If you have not yet installed this package, proceed as follows:

Create a new script, copy/paste the following code and run it:

6.6 SIA 180 Thermal Comfort

6.6.1 Task

You want to plot a diagram like the one from the SIA 180:2014 which showes

  • scatter plot of indoor- and outdoor temperature sensor data (indoor mean of day, outdoor mean of last 48 hours)

  • points colored according to season

  • different comfort lines

6.6.2 Basis

  • A csv file with time series from multiple temperature and humidity sensors in °C

  • A csv file with the outdoor temperature

6.6.3 Solution

The sensor data is not in a constant intervall and not yet aggregated. So after reading in the time series the data has to get filtered, aggregated per day and merged.

Create a new script, copy/paste the following code and run it:

library(redutils)
library(dplyr)
library(lubridate)
library(zoo)
library(plotly)

# load time series data and aggregate mean values
dfTempOa <- read.csv("https://github.com/hslu-ige-laes/edar/raw/master/sampleData/centralOutsideTemp.csv",
                          stringsAsFactors=FALSE,
                          sep =";")

dfTempOa$time <- parse_date_time(dfTempOa$time,
                                      order = "YmdHMS",
                                      tz = "UTC")

dfTempOa$hour <- cut(dfTempOa$time, breaks = "hour")

dfTempOa <- dfTempOa %>%
  group_by(hour) %>%
  mutate(tempMean = mean(centralOutsideTemp)) %>%
  ungroup() %>% 
  select(time, tempMean) %>% 
  unique()

# Fill missing values with NA
grid.df <- data.frame(time = seq(min(dfTempOa$time, na.rm = TRUE),
                                 max(dfTempOa$time, na.rm = TRUE),
                                 by = "hour"))
dfTempOa <- merge(dfTempOa, grid.df, all = TRUE)

dfTempOa <- dfTempOa %>%
  mutate(tempOa = rollmean(tempMean, 48, fill = NA, align = "right"))

dfTempOa <- dfTempOa %>%
  select(time, tempOa) %>%
  unique() %>%
  na.omit()


dfTempR <- read.csv("https://github.com/hslu-ige-laes/edar/raw/master/sampleData/flatTempHum.csv",
                       stringsAsFactors=FALSE,
                       sep =";")

dfTempR$time <- parse_date_time(dfTempR$time,
                                   order = "YmdHMS",
                                   tz = "UTC")

# select temperature and humidity and remove empty cells
dfTempR <- dfTempR %>% select(time, FlatA_Temp) %>% na.omit()

dfTempR$hour <- cut(dfTempR$time, breaks = "hour")

dfTempR <- dfTempR %>%
  group_by(hour) %>%
  mutate(tempR = mean(FlatA_Temp)) %>%
  ungroup() %>% 
  select(time, tempR) %>% 
  unique()

# Fill missing values with NA
grid.df <- data.frame(time = seq(min(dfTempR$time, na.rm = TRUE),
                                 max(dfTempR$time, na.rm = TRUE),
                                 by = "hour"))
dfTempR <- merge(dfTempR, grid.df, all = TRUE)

data <- merge(dfTempR, dfTempOa, all = TRUE) %>% unique() %>% na.omit()

data$season <- redutils::getSeason(data$time)

# plot diagram

# axis properties
minx <- floor(min(0, min(data$tempOa)))
maxx <- ceiling(max(28, max(data$tempOa)))

miny <- floor(min(21.0,min(data$tempR)))-1
maxy <- ceiling(max(32.0,max(data$tempR)))+1

# line setpoint heat
df.heatSp <- data.frame(tempOa = c(minx, 19, 23.5, maxx), tempR = c(20.5, 20.5, 22, 22))

# line setpoint cool according to SIA 180:2014 Fig. 4
df.coolSp1 <- data.frame(tempOa = c(minx, 12, 17.5, maxx),tempR = c(24.5, 24.5, 26.5, 26.5))

# line setpoint cool according to SIA 180:2014 Fig. 3
df.coolSp2 <- data.frame(tempOa = c(minx, 10, maxx),tempR = c(25, 25, 0.33 * maxx + 21.8))

data %>%
  plot_ly(showlegend = TRUE) %>%
  add_lines(data = df.coolSp2,
            x = ~tempOa,
            y = ~tempR,
            name = "Upper limit SIA 180 passive cooling",
            opacity = 0.7,
            color = "#FDE725FF",
            hoverinfo = "text",
            text = ~ paste("Upper limit SIA 180 passive cooling",
                           "<br />TempR:   ", sprintf("%.1f \u00B0C", tempR),
                           "<br />TempOa: ", sprintf("%.1f \u00B0C", tempOa)
            )
  ) %>%
  add_lines(data = df.coolSp1,
            x = ~tempOa,
            y = ~tempR,
            name = "Upper limit SIA 180 active cooling",
            opacity = 0.7,
            color = "#1E9B8AFF",
            hoverinfo = "text",
            text = ~ paste("Upper limit SIA 180 active cooling",
                           "<br />TempR:   ", sprintf("%.1f \u00B0C", tempR),
                           "<br />TempOa: ", sprintf("%.1f \u00B0C", tempOa)
            )        ) %>%
  add_lines(data = df.heatSp,
            x = ~tempOa,
            y = ~tempR,
            name = "Lower Limit SIA 180",
            opacity = 0.7,
            color = "#440154FF",
            hoverinfo = "text",
            text = ~ paste("Lower Limit SIA 180",
                           "<br />TempR:   ", sprintf("%.1f \u00B0C", tempR),
                           "<br />TempOa: ", sprintf("%.1f \u00B0C", tempOa)
            )        ) %>%
  add_markers(data = data %>% filter(season == "Spring"),
              x = ~tempOa,
              y = ~tempR,
              name = "Spring",
              marker = list(color = "#2db27d", opacity = 0.1),
              hoverinfo = "text",
              text = ~ paste("TempR:   ", sprintf("%.1f \u00B0C", tempR),
                             "<br />TempOa: ", sprintf("%.1f \u00B0C", tempOa),
                             "<br />Date:       ", time,
                             "<br />Season:  ", season
              )
  ) %>%
  add_markers(data = data %>% filter(season == "Summer"),
              x = ~tempOa,
              y = ~tempR,
              name = "Summer",
              marker = list(color = "#febc2b", opacity = 0.1),
              hoverinfo = "text",
              text = ~ paste("TempR:   ", sprintf("%.1f \u00B0C", tempR),
                             "<br />TempOa: ", sprintf("%.1f \u00B0C", tempOa),
                             "<br />Date:       ", time,
                             "<br />Season:  ", season
              )
  ) %>%
  add_markers(data = data %>% filter(season == "Fall"),
              x = ~tempOa,
              y = ~tempR,
              name = "Fall",
              marker = list(color = "#440154", opacity = 0.1),
              hoverinfo = "text",
              text = ~ paste("TempR:   ", sprintf("%.1f \u00B0C", tempR),
                             "<br />TempOa: ", sprintf("%.1f \u00B0C", tempOa),
                             "<br />Date:       ", time,
                             "<br />Season:  ", season
              )
  ) %>%
  add_markers(data = data %>% filter(season == "Winter"),
              x = ~tempOa,
              y = ~tempR,
              name = "Winter",
              marker = list(color = "#365c8d", opacity = 0.1),
              hoverinfo = "text",
              text = ~ paste("TempR:   ", sprintf("%.1f \u00B0C", tempR),
                             "<br />TempOa: ", sprintf("%.1f \u00B0C", tempOa),
                             "<br />Date:       ", time,
                             "<br />Season:  ", season
              )
  ) %>%
  layout(
    xaxis = list(title = "Moving average outdoor temperature in \u00B0C over 48 hours",
                 range = c(minx, maxx),
                 zeroline = FALSE,
                 tick0 = minx,
                 dtick = 2,
                 titlefont = list(size = 14, color = "darkgrey")),
    yaxis = list(title = "Room Temperature in \u00B0C",
                 range = c(miny, maxy),
                 dtick = 1,
                 titlefont = list(size = 14, color = "darkgrey")),
    hoverlabel = list(align = "left"),
    margin = list(l = 80, t = 50, r = 50, b = 10),
    legend = list(orientation = 'h',
                  x = 0.0,
                  y = -0.3)
  ) %>%
  
  plotly::config(modeBarButtons = list(list("toImage")),
                 displaylogo = FALSE,
                 toImageButtonOptions = list(
                   format = "svg"
                 )
  )

6.6.4 Discussion

tbd

6.6.5 See Also

tbd

6.7 Electricity Consumption Residential

6.7.1 Task

You want to plot an electricity consumption diagram which shows

  • upper plot with daily energy consumption in kWh/day
  • lower plot with standby-losses in Watts

Additionaly we would like to see the consumption of an average Swiss household.

6.7.2 Basis

  • A csv file with time series of an electric meter in 15 minute interval.

6.7.3 Solution

Create a new script, copy/paste the following code and run it:

library(redutils)
library(dplyr)
library(lubridate)
library(zoo)
library(plotly)

# load time series data and aggregate mean values
df <- read.csv("https://github.com/hslu-ige-laes/edar/raw/master/sampleData/flatElectricity.csv",
               stringsAsFactors=FALSE,
               sep =";")

df$time <- parse_date_time(df$time,
                           order = "YmdHMS",
                           tz = "UTC")

# select room
df <- df %>% select(time, FlatC_Ele)

# rename columns
colnames(df) <- c("timestamp", "meterValue")

# filter timerange
df <- df %>% filter(timestamp > "2019-07-01")

# Fill missing values with NA
grid.df <- data.frame(timestamp = seq(min(df$timestamp, na.rm = TRUE),
                                      max(df$timestamp, na.rm = TRUE),
                                      by = "15 mins"))
df <- merge(df, grid.df, all = TRUE)

# convert steadily counting energy meter value from kWh to power in kW
df <- df %>% 
  mutate(value = (meterValue - lag(meterValue))) %>%
  select(-meterValue)

# remove negative values which occur beause of change summer/winter time
df <- df %>% filter(value >= 0)

# determine date related parameters for later filtering
df$day <- as.Date(df$time, tz = "UTC")
df$week <- lubridate::week(df$time)
df$month <- lubridate::month(df$time)
df$year <- lubridate::year(df$time)

# data cleansing
# tag NA
df <- df %>% mutate(deleteNA = ifelse(is.na(value),1,0))

# tag values below 0 and higher than 9.2 kW
df <- df %>% mutate(deleteHiLoVal = ifelse(value > 9.2,1, ifelse(value < 0,1,0)))
# Assumption max. fuse 40 ampere (higher fuses for single family houses)
# this results in continuous power 9.2 kW
# this results in an hourly consumption of 9.2kWh
# over 24h = approx. 221 kWh max. consumption per day

# tag whole days which have one or more values to delete, keep only whole valid days
df <- df %>%
  group_by(day) %>% 
  mutate(delete = sum(deleteNA, na.rm = TRUE) + sum(deleteHiLoVal, na.rm = TRUE))

df <- df %>% ungroup()

# delete full days with invalid data
df <- df %>%
  filter(delete == 0) %>%
  select(-deleteNA, -deleteHiLoVal, -delete)

# determine season for later filtering
df <- df %>% mutate(season = redutils::getSeason(timestamp))

# calculate sum and min per day
df <- df %>% group_by(day) %>% mutate(sum = sum(value))
df <- df %>% group_by(day) %>% mutate(min = min(value)*1000*4)
df <- df %>% ungroup()

df <- df %>% select(day, sum, min, season) %>% unique()

df <- df %>% mutate(ravgUsage = zoo::rollmean(x=sum, 7, fill = NA))
df <- df %>% mutate(rminStandby = -1 * zoo::rollmaxr(x = -1 * min, 7, fill = NA))

typEleConsVal <- redutils::getTypEleConsHousehold(occupants = 2, rooms = 3.5, bldgType = "multi", laundry = "hotWaterSupply")/365


# Plot

minY <- 0
maxYUsage <- max(df %>% select(sum), na.rm=TRUE)
maxYUsage <- max(maxYUsage, typEleConsVal/365)
maxYStandby <- max(max(df %>% select(min), na.rm=TRUE), 0.25*maxYUsage/24*1000)
minX <- min(df$day)
maxX <- max(df$day)
averageUsage <- mean(df$sum, na.rm=TRUE)
averageStandby <- mean(df$rminStandby, na.rm=TRUE)
shareStandby <- nrow(df %>% select(sum) %>% na.omit()) * averageStandby * 24 / (1000 * sum(df$sum, na.rm=TRUE)) * 100

# legend
l <- list(
  orientation = "h",
  tracegroupgap = "20",
  font = list(size = 8),
  xanchor = "center",
  x = 0.5,
  itemclick = FALSE
)

fig1 <- df %>%
  plot_ly(x = ~day, showlegend = TRUE) %>%
  add_trace(data = df %>% filter(season == "Spring"),
            type = "bar",
            y = ~sum,
            name = "Spring",
            legendgroup = "group1",
            marker = list(color = "#2db27d", opacity = 0.2),
            hoverinfo = "text",
            text = ~ paste("<br />daily usage:              ", sprintf("%.1f kWh/d", sum),
                           "<br />rolling average:        ", sprintf("%.1f kWh/d", ravgUsage),
                           "<br />Average vis. points: ", sprintf("%.1f kWh/d", averageUsage),
                           "<br />Date:                        ", day,
                           "<br />Season:                   ", season
            )
  ) %>% 
  add_trace(data = df %>% filter(season == "Summer"),
            type = "bar",
            y = ~sum,
            name = "Summer",
            legendgroup = "group1",
            marker = list(color = "#febc2b", opacity = 0.2),
            hoverinfo = "text",
            text = ~ paste("<br />rolling average:        ", sprintf("%.1f kWh/d", ravgUsage),
                           "<br />Average vis. points: ", sprintf("%.1f kWh/d", averageUsage),
                           "<br />Date:                        ", day,
                           "<br />Season:                   ", season
            )
  ) %>% 
  add_trace(data = df %>% filter(season == "Fall"),
            type = "bar",
            y = ~sum,
            name = "Fall",
            legendgroup = "group1",
            marker = list(color = "#440154", opacity = 0.2),
            hoverinfo = "text",
            text = ~ paste("<br />rolling average:        ", sprintf("%.1f kWh/d", ravgUsage),
                           "<br />Average vis. points: ", sprintf("%.1f kWh/d", averageUsage),
                           "<br />Date:                        ", day,
                           "<br />Season:                   ", season
            )
  ) %>% 
  add_trace(data = df %>% filter(season == "Winter"),
            type = "bar",
            y = ~sum,
            name = "Winter",
            legendgroup = "group1",
            marker = list(color = "#365c8d", opacity = 0.2),
            hoverinfo = "text",
            text = ~ paste("<br />rolling average:        ", sprintf("%.1f kWh/d", ravgUsage),
                           "<br />Average vis. points: ", sprintf("%.1f kWh/d", averageUsage),
                           "<br />Date:                        ", day,
                           "<br />Season:                   ", season
            )
  ) %>%
  add_trace(data = df,
            type = "scatter",
            mode = "markers",
            y = ~ravgUsage,
            name = "Average Cons. (7 days)",
            legendgroup = "group2",
            marker = list(color = "orange", opacity = 0.4, symbol = "circle"),
            hoverinfo = "text",
            text = ~ paste("<br />rolling average:        ", sprintf("%.1f kWh/d", ravgUsage),
                           "<br />Average vis. points: ", sprintf("%.1f kWh/d", averageUsage),
                           "<br />Date:                        ", day,
                           "<br />Season:                   ", season
            )
  ) %>% 
  add_segments(x = ~minX,
               xend = ~maxX,
               y = ~averageUsage,
               yend = ~averageUsage,
               name = "Average Cons. Total",
               legendgroup = "group2",
               line = list(color = "orange", opacity = 1.0, dash = "dot"),
               hoverinfo = "text",
               text = ~ paste("<br />rolling average:        ", sprintf("%.1f kWh/d", ravgUsage),
                              "<br />Average vis. points: ", sprintf("%.1f kWh/d", averageUsage),
                              "<br />Date:                        ", day,
                              "<br />Season:                   ", season
               )
  ) %>% 
  add_segments(x = ~minX,
               xend = ~maxX,
               y = ~averageStandby*24/1000,
               yend = ~averageStandby*24/1000,
               name = "Average Standby Total",
               legendgroup = "group3",
               showlegend = FALSE,
               line = list(color = "black", opacity = 1.0, dash = "dot"),
               hoverinfo = "text",
               text = ~ paste("<br />Average standby power:          ", sprintf("%.0f W", averageStandby),
                              "<br />equals to daily energy:         ", sprintf("%.1f kWh", averageStandby*24/1000),
                              "<br />Standby percent of total cons.: ", sprintf("%.0f %%", shareStandby)
               )
  ) %>% 
  add_segments(x = ~minX,
               xend = ~maxX,
               y = ~typEleConsVal,
               yend = ~typEleConsVal,
               name = "typical household",
               legendgroup = "group4",
               line = list(color = "#481567FF", opacity = 1.0, dash = "dot"),
               hoverinfo = "text",
               text = ~ paste("<br />typical household:           ", sprintf("%.0f kWh/year", typEleConsVal*365),
                              "<br />equals to daily energy:      ", sprintf("%.1f kWh/day", typEleConsVal),
                              "<br />consumption of current flat: ", sprintf("%.1f kWh/day", averageUsage)
               )
  ) %>% 
  add_annotations(
    x = minX,
    y = typEleConsVal,
    text = paste0("typical comparable household ", sprintf("%.1f kWh/d", typEleConsVal)),
    xref = "x",
    yref = "y",
    showarrow = TRUE,
    arrowhead = 7,
    ax = 100,
    ay = -20,
    font = list(color = "#481567FF")
  ) %>% 
  add_annotations(
    x = maxX,
    y = averageUsage,
    text = paste0("Average consumption ", sprintf("%.1f kWh/d", averageUsage)),
    xref = "x",
    yref = "y",
    showarrow = TRUE,
    arrowhead = 7,
    ax = -100,
    ay = -60,
    font = list(color = "orange")
  ) %>% 
  add_annotations(
    x = maxX,
    y = averageStandby*24/1000,
    text = paste0(sprintf("%.1f %%", shareStandby), " of the consumption are standby-losses"),
    xref = "x",
    yref = "y",
    showarrow = TRUE,
    arrowhead = 7,
    ax = -160,
    ay = -15,
    font = list(color = "black")
  ) %>% 
  layout(
    xaxis = list(
      title = ""
    ),
    yaxis = list(title = "Consumption<br>(kWh/d)",
                 range = c(minY, maxYUsage),
                 titlefont = list(size = 14, color = "darkgrey")),
    hoverlabel = list(align = "left"),
    margin = list(l = 80, t = 50, r = 50, b = 10),
    legend = l
  )

fig2 <- df %>%
  plot_ly(x = ~day, showlegend = TRUE) %>%
  add_trace(data = df,
            type = "bar",
            y = ~min,
            name = "Daily standby-losses",
            legendgroup = "group3",
            marker = list(color = "darkgrey", opacity = 0.2),
            hoverinfo = "text",
            text = ~ paste("<br />daily standby:           ", sprintf("%.0f W", min),
                           "<br />rolling average:        ", sprintf("%.0f W", rminStandby),
                           "<br />Average vis. points: ", sprintf("%.0f W", averageStandby),
                           "<br />Date:                        ", day,
                           "<br />Season:                   ", season
            )
  ) %>% 
  add_trace(data = df,
            type = "scatter",
            mode = "markers",
            y = ~rminStandby,
            name = "Average Standby (7 days)",
            legendgroup = "group3",
            marker = list(color = "darkgrey", opacity = 0.5, symbol = "circle"),
            hoverinfo = "text",
            text = ~ paste("<br />daily standby:           ", sprintf("%.0f W", min),
                           "<br />rolling average:        ", sprintf("%.0f W", rminStandby),
                           "<br />Average vis. points: ", sprintf("%.0f W", averageStandby),
                           "<br />Date:                        ", day,
                           "<br />Season:                   ", season
            )
  ) %>% 
  add_segments(x = ~minX,
               xend = ~maxX,
               y = ~averageStandby,
               yend = ~averageStandby,
               name = "Average Standby Total",
               legendgroup = "group3",
               line = list(color = "black", opacity = 1.0, dash = "dot"),
               hoverinfo = "text",
               text = ~ paste("<br />Average standby power:          ", sprintf("%.0f W", averageStandby),
                              "<br />equals to daily energy:         ", sprintf("%.1f kWh", averageStandby*24/1000),
                              "<br />Standby percent of total cons.: ", sprintf("%.0f %%", shareStandby)
               )
  ) %>% 
  add_annotations(
    x = maxX,
    y = averageStandby,
    text = paste0(sprintf("%.0f W", averageStandby), " standby-losses on average"),
    xref = "x",
    yref = "y",
    showarrow = TRUE,
    arrowhead = 7,
    ax = -60,
    ay = -20,
    font = list(color = "black")
  ) %>% 
  layout(
    xaxis = list(
      title = ""
    ),
    yaxis = list(title = " Standby<br>(W)",
                 range = c(minY, maxYStandby),
                 titlefont = list(size = 14, color = "darkgrey"),
                 legend = list(orientation = 'h')),
    legend = l
  )

# calculate ratio which is visual representative for comparison 
# ratio <- 1/maxYUsage * maxYStandby * 24 / 1000
ratio <- 0.3
fig <- subplot(fig1, fig2, nrows = 2, shareX = TRUE, heights = c(1-ratio, ratio), titleY = TRUE) %>%
  plotly::config(modeBarButtons = list(list("toImage")),
                 displaylogo = FALSE,
                 toImageButtonOptions = list(
                   format = "svg"
                 )
  )
fig

6.7.4 Discussion

tbd

6.7.5 See Also

tbd